算法之我见 [康托展开] 排列编号
问题描述:对于1~N的序列来说,将他的全排列升序编号,问第n排列是怎样的,或者给一个排列问是第几个序列?
关键思想:这种问题可以用康托展开来解决,其关键思想其实很简单,说白了就是简单的排列组合。
我们先来写下1~4的全排列感受一下{
1 2 3 4
1 2 4 3
1 3 2 4
1 3 4 2
1 4 2 3
1 4 3 2
然后依次枚举2、3、4开头的,不多写了。
}
我们想一下,枚举的时候是在前几位尽量找小的开始枚举,确定该位后,下一位也要尽可能小,就这样枚举。
于是有这样一种方法来算出序号。
我们找到第i位,在右边剩下N-i位数数有几个比第i位小的,这些小的在第i位的排列此前已经历数过了,很关键,我们就加上去。
核心思想就是这个了。确定i位的时候,剩下N-i位有(N-i)!种排列。
代码如下:
#include <iostream> #include <string> using namespace std; const int MAXN=10; int fac[10]={1,1,2,6,24,120,720,5040,40320,362880}; int EnKT(string s,int N){ int ans=1,cnt; for(int i=0;i<N;i++){ cnt=0; for(int j=i+1;j<N;j++){//找到后面比较小的数,他们在i处的排列已经历数过 if(s[j]<s[i])cnt++; } ans+=cnt*fac[N-i-1]; } return ans; } string DeKT(int N,int n){//N的排列第n个序列 string ans=""; n--; bool vis[MAXN]={false}; for(int i=N-1;i>=0;i--){ int order=n/fac[i];//0对应最小未使用;1对应第二大未使用 for(int j=1;j<=N;j++){ if(!vis[j]){ if(order==0){ ans+='0'+j; vis[j]=true; break; }else order--; } } n%=fac[i]; } return ans; } int main(){ int n; string t; cin>>n>>t; cout<<EnKT(t,n)<<endl; cout<<DeKT(5,7)<<endl; return 0; }
边完善自己边认识自己